package com.yycx.module.pay.provider.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.egzosn.pay.ali.api.AliPayConfigStorage;
import com.egzosn.pay.common.api.PayConfigStorage;
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.wx.api.WxPayConfigStorage;
import com.yycx.common.base.entity.EntityMap;
import com.yycx.common.base.handler.PayInfoHandler;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.common.enums.OrderStatusEnum;
import com.yycx.common.enums.PayStatusEnum;
import com.yycx.common.mybatis.base.service.impl.BaseServiceImpl;
import com.yycx.common.mybatis.model.ResultBody;
import com.yycx.common.mybatis.query.CriteriaQuery;
import com.yycx.common.mybatis.query.CriteriaSave;
import com.yycx.common.security.OpenHelper;
import com.yycx.common.utils.*;
import com.yycx.module.pay.client.entity.PayConfig;
import com.yycx.module.pay.client.entity.PayInfo;
import com.yycx.module.pay.client.enums.PayType;
import com.yycx.module.pay.provider.mapper.PayInfoMapper;
import com.yycx.module.pay.provider.response.PayResponse;
import com.yycx.module.pay.provider.service.PayBalanceLogService;
import com.yycx.module.pay.provider.service.PayBalanceService;
import com.yycx.module.pay.provider.service.PayConfigService;
import com.yycx.module.pay.provider.service.PayInfoService;
import com.yycx.module.pay.provider.strategy.base.BasePayStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;

/**
 * 支付信息接口实现类
 *
 * @author flyme
 * @date 2019-06-23
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class PayInfoServiceImpl extends BaseServiceImpl<PayInfoMapper, PayInfo> implements PayInfoService {

    @Autowired
    private PayConfigService payConfigService;
    @Autowired
    private OrderNoUtil orderNoUtil;
    @Autowired
    private PayBalanceService balanceService;
    @Autowired
    private PayBalanceLogService balanceLogService;

    @Autowired
    private Map<String, BasePayStrategy> basePayStrategyMap;

    @Override
    public ResultBody createPayInfo(Map params) {
        CriteriaSave cs = new CriteriaSave(params, PayInfo.class);
        Long companyId = OpenHelper.getCompanyId();
        //订单号，当不传递订单号时返回自动赋值的订单号
        String outTradeNo = Optional.ofNullable((String) params.get("outTradeNo")).orElse(orderNoUtil.getOrderNo());
        params.put("outTradeNo",outTradeNo);
        ResultBody resultBody = commonBeanUtils.getOrder(outTradeNo);
        if (FlymeUtils.isEmpty(resultBody)) {
            //支付状态
            cs.put("payStatus", PayStatusEnum.NOPAY.code);
            //订单状态
            cs.put("orderStatus", OrderStatusEnum.UNPAY.code);
            cs.put("companyId", companyId);
            cs.put("userId", OpenHelper.getUserId());
            //订单号
            cs.put("outTradeNo", outTradeNo);
            resultBody = savePayInfo(cs, params, outTradeNo);
            resultBody.setExtra(cs.getRequestMap());
            if (resultBody.isOk()) {
                redisUtils.set("ORDER_" + outTradeNo, resultBody, 1800);
                resultBody.setMsg("订单创建成功");
            }
        }
        return resultBody;
    }


    /**
     * 保存支付信息到数据库
     */
    private ResultBody savePayInfo(CriteriaSave cs, Map params, String outTradeNo) {
        //获取订单信息自定义handler
        PayInfoHandler payInfoHandler = SpringContextHolder.getHandler(cs.getHandlerName(), PayInfoHandler.class);
        ResultBody resultBody = new ResultBody();
        //是否支持扩展条件
        boolean hasHandler = false;
        Boolean isSave = false;
        //保存对象
        PayInfo payInfo = null;
        if (ObjectUtils.isNotNull(payInfoHandler)) {
            resultBody = payInfoHandler.validate(cs, cs.getRequestMap());
            if (resultBody.isOk() == false) {
                return resultBody;
            }
            payInfoHandler.prepare(cs, cs.getRequestMap());
            //获取自定义实体
            payInfo = payInfoHandler.getPayInfo(cs, cs.getRequestMap(), outTradeNo);
            Object busId = params.get("busId");
            if (FlymeUtils.isNotEmpty(busId)) {
                PayInfo old = getByProperty("busId", busId);
                if (FlymeUtils.isNotEmpty(old)) {
                    payInfo.setPayInfoId(old.getPayInfoId());
                }
            }
            if (ObjectUtils.isNotEmpty(payInfo)) {
                if (cs.getSaveToRedis() == true) {
                    //保存至redis时isSave要设置为true来执行complete事件
                    isSave = true;
                } else {
                    //保存原始对象
                    isSave = saveOrUpdate(payInfo);

                }
            }
            hasHandler = true;
        }
        //当handler.getEntity对象为空时执行默认保存
        if (!isSave && FlymeUtils.isEmpty(payInfo)) {
            //保存原始对象
            payInfo = (PayInfo) JsonUtils.jsonToBean(JSONUtil.toJsonStr(cs.getRequestMap()), cs.getCls());
            isSave = saveOrUpdate(payInfo);
        }

        if (isSave) {
            if (hasHandler) {
                //保存成功后调用扩展事件
                payInfoHandler.complete(cs, cs.getRequestMap(), payInfo);
            }
            resultBody.data(payInfo);
        }
        return resultBody.setMsg("保存成功");

    }


    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public PayInfo findByOutTradeNo(String outTradeNo) {
        CriteriaQuery<PayInfo> cq = new CriteriaQuery(PayInfo.class);
        return getOne(cq.eq(true, "outTradeNo", outTradeNo), false);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public PayInfo findByBusId(Long busId) {
        CriteriaQuery<PayInfo> cq = new CriteriaQuery(PayInfo.class);
        return getOne(cq.eq(true, "busId", busId), false);
    }


    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public PayInfo findNoPayOrder(Class cls, Long companyId) {
        CriteriaQuery<PayInfo> cq = new CriteriaQuery(PayInfo.class);
        return getOne(cq.eq(true, "orderEntity", cls.getSimpleName()).eq(true, "companyId", companyId).eq("payStatus", PayStatusEnum.NOPAY.code), false);
    }


    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody beforePageList(CriteriaQuery<PayInfo> cq, PayInfo payInfo, EntityMap requestMap) {
        cq.select(PayInfo.class, "payInfoId", "busId", "orderEntity", "orderTitle", "companyId", "orderBody", "payStatus", "orderAmount", "invoiceId", "outTradeNo", "orderStatus");
        Integer invoiceState = cq.getRequestMap().getInt("invoiceState", 0);
        if (FlymeUtils.isNotEmpty(invoiceState)) {
            if (invoiceState.equals(1)) {
                cq.isNotNull("invoiceId");
            } else {
                cq.isNull("invoiceId");
            }
        }
        cq.eq(PayInfo.class, "orderStatus");
        cq.eq(PayInfo.class, "orderBody");
        cq.eq(PayInfo.class, "payStatus");
        return super.beforePageList(cq, payInfo, requestMap);
    }

    /**
     * @param orderId
     * @param invoiceId
     * @return
     */
    @Override
    public Boolean updateInvoiceId(Long orderId, Long invoiceId) {
        UpdateWrapper uw = new UpdateWrapper();
        uw.set(true, "invoiceId", invoiceId);
        uw.eq("orderId", orderId);
        return update(uw);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody checkPayState(Map params) {
        CriteriaQuery<PayInfo> cq = new CriteriaQuery(params, PayInfo.class);
        PayInfo payInfo = cq.getEntity(PayInfo.class);
        Long payInfoId = payInfo.getPayInfoId();
        String outTradeNo = payInfo.getOutTradeNo();
        Long busId = payInfo.getBusId();
        ApiAssert.anyOneIsNotNull("查询参数不能为空", outTradeNo, payInfoId, busId);
        if (FlymeUtils.isNotEmpty(outTradeNo)) {
            cq.eq("outTradeNo", outTradeNo);
        }
        if (FlymeUtils.isNotEmpty(payInfoId)) {
            cq.eq("payInfoId", payInfoId);
        }
        if (FlymeUtils.isNotEmpty(busId)) {
            cq.eq("busId", busId);
        }
        cq.select(PayInfo.class, "busId", "orderAmount", "payInfoId", "orderTitle", "orderBody", "payStatus", "payType", "outTradeNo");
        EntityMap result = findOne(cq);
        Integer payStatus = result.getInt("payStatus", 0);
        if (payStatus.equals(PayStatusEnum.PAY.code)) {
            return ResultBody.ok("支付成功", result);
        } else {
            return ResultBody.ok("未支付", result);
        }

    }

    @Override
    public Boolean clearInvoiceId(Long[] invoiceIds) {
        UpdateWrapper uw = new UpdateWrapper();
        uw.set(true, "invoiceId", null);
        uw.in("invoiceId", invoiceIds);
        return update(uw);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Boolean validate(String outTradeNo) {
        PayInfo payInfo = findByOutTradeNo(outTradeNo);
        if (FlymeUtils.isNotEmpty(payInfo)) {
            Integer payStatusEnum = payInfo.getPayStatus();
            if (PayStatusEnum.NOPAY.code.equals(payStatusEnum)) {
                return true;
            } else {
                return false;
            }
        }
        log.info("微信支付回调:校验失败");
        return false;
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public EntityMap getOrderParams(String outTradeNo) {
        ResultBody resultBody = commonBeanUtils.getOrder(outTradeNo);
        return Optional.ofNullable(resultBody).orElse(new ResultBody()).getExtra();
    }

    @Override
    public Boolean success(Map<String, Object> content, Integer orderState) {
        Object outTradeNo = content.get("outTradeNo");
        Boolean tag = false;
        if (FlymeUtils.isNotEmpty(outTradeNo)) {
            UpdateWrapper updateWrapper = new UpdateWrapper();
            updateWrapper.set(true, "payStatus", PayStatusEnum.PAY.code);
            updateWrapper.set(true, "payDate", DateUtils.getDateTime());
            updateWrapper.set(true, "orderStatus", orderState);
            updateWrapper.set(true, "fromPay", content.get("fromPay"));
            updateWrapper.set(true, "payType", content.get("payType"));
            updateWrapper.set(true, "transactionType", content.get("transactionType"));

            if(FlymeUtils.isEmpty(content.get("tradeNo"))){
                updateWrapper.set(true,"tradeNo",content.get("transactionId"));
            }else{
                updateWrapper.set(true,"tradeNo",content.get("tradeNo"));
            }
            updateWrapper.eq(true, "outTradeNo", content.get("outTradeNo"));
            tag = update(updateWrapper);
            if (tag) {
                //删除缓存订单
                redisUtils.del("ORDER_" + outTradeNo.toString());
            }
        }
        return tag;
    }

    @Override
    public BigDecimal sumPayStatus(Long userId, Integer payStatus) {
        CriteriaQuery cq = new CriteriaQuery(PayInfo.class);
        cq.addSelect("COALESCE(sum(orderAmount),0) amount");
        cq.eq("payStatus", payStatus);
        cq.eq("userId", userId);
        EntityMap one = findOne(cq);
        BigDecimal amount = one.getBigDecimal("amount");
        return amount;
    }


    @Override
    public ResultBody getPayInfo(Map params) {
        Long payId = MapUtil.getLong(params, "payId");
        ApiAssert.isNotEmpty("请选择支付方式", payId);
        String handlerName = MapUtil.getStr(params, "handlerName");
        ApiAssert.isNotEmpty("支付逻辑处理器不能为空", handlerName);
        String outTradeNo = MapUtil.getStr(params, "outTradeNo");
        //支付响应结果
        PayResponse payResponse = payConfigService.getPayResponse(outTradeNo,payId, handlerName);
        ResultBody resultBody = createPayInfo(params);
        ApiAssert.isNotEmpty("订单号不能为空", outTradeNo);
        if (resultBody.isOk()) {
            PayInfo payInfo = (PayInfo) resultBody.getData();
            Long orderUserId = payInfo.getUserId();
            String title = payInfo.getOrderTitle();
            String orderBody = payInfo.getOrderBody();
            //支付配置
            PayConfig payConfig = payResponse.getPayConfig();
            //交易类型
            String tradeType = payConfig.getTransactionType();
            //订单金额
            BigDecimal orderAmount = FlymeUtils.getBigDecimal(payInfo.getOrderAmount(), "0");
            ApiAssert.gtzero("订单金额必须大于0", orderAmount);
            if (FlymeUtils.isNotEmpty(payConfig) && payConfig.getTestAmount()) {
                orderAmount = new BigDecimal("0.01");
            }
            String payType = payResponse.getStorage().getPayType();
            PayOrder payOrder = new PayOrder(title, orderBody, orderAmount, outTradeNo.toString());
            String addition = payInfo.getAttach();
            if (FlymeUtils.isNotEmpty(addition)) {
                payOrder.setAddition(addition);
            }
            EntityMap extra = new EntityMap();
            Object resultInfo = null;
            EntityMap result = new EntityMap(params);
            result.put("payTime", DateUtils.getNowDateTime());
            result.put("outTradeNo", outTradeNo);
            result.put("payId", payId);
            //设置支付类型和交易类型
            payOrder.setTransactionType(PayType.valueOf(payType).getTransactionType(tradeType));
            // 重新设置支付成功return地址
            if(FlymeUtils.isNotEmpty(payConfig.getReturnUrl())){
                PayConfigStorage store = payResponse.getStorage();
                if(store instanceof WxPayConfigStorage){
                    WxPayConfigStorage wxStore = (WxPayConfigStorage)store;
                    wxStore.setReturnUrl(String.format(payConfig.getReturnUrl(),
                            payInfo.getOrderBody(),
                            payInfo.getOutTradeNo(),
                            payInfo.getOrderAmount(),
                            payConfig.getPayName()));
                }else if(store instanceof AliPayConfigStorage){
                    AliPayConfigStorage aliStore = (AliPayConfigStorage)store;
                    aliStore.setReturnUrl(String.format(payConfig.getReturnUrl()+outTradeNo,
                            payInfo.getOrderBody(),
                            payInfo.getOutTradeNo(),
                            payInfo.getOrderAmount(),
                            payConfig.getPayName()));
                }
            }

            if (FlymeUtils.isNotEmpty(basePayStrategyMap)) {
                for (BasePayStrategy basePayStrategy : basePayStrategyMap.values()) {
                    if (basePayStrategy.support(payType, tradeType)) {
                        basePayStrategy.getPayResult(resultBody, result, payResponse, payOrder, tradeType);
                    }
                }
            }
            return ResultBody.ok("下单成功", result).setExtra(extra);
        } else {
            return resultBody;
        }


    }

}
